package com.mdp.workflow.ru.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdp.core.entity.Result;
import com.mdp.core.entity.Tips;
import com.mdp.core.err.BizException;
import com.mdp.core.query.QueryTools;
import com.mdp.core.service.BaseService;
import com.mdp.core.utils.BaseUtils;
import com.mdp.form.entity.FormDataVo;
import com.mdp.micro.client.CallBizService;
import com.mdp.safe.client.entity.User;
import com.mdp.safe.client.utils.LoginUtils;
import com.mdp.workflow.api.ExecutionService;
import com.mdp.workflow.pub.service.SysUserService;
import com.mdp.workflow.re.entity.ProcdefNodeInfo;
import com.mdp.workflow.ru.entity.Execution;
import com.mdp.workflow.ru.entity.ProcinstNodeInfo;
import com.mdp.workflow.ru.entity.ProcinstParames;
import com.mdp.workflow.ru.listener.PushImMsgService;
import com.mdp.workflow.ru.mapper.ExecutionMapper;
import com.mdp.workflow.ru.vo.ProcStartVo;
import com.mdp.workflow.re.service.ProcdefNodeInfoService;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 父类已经支持增删改查操作,因此,即使本类什么也不写,也已经可以满足一般的增删改查操作了.<br> 
 * 组织 com.mdp  顶级模块 workflow 大模块 ru 小模块 <br>
 * 实体 Execution 表 act_ru_execution 当前主键(包括多主键): id_; 
 ***/
@Service("mdp.workflow.ru.ExecutionServiceImpl")
public class ExecutionServiceImpl extends BaseService<ExecutionMapper,Execution> implements  ExecutionService{
	static Logger logger =LoggerFactory.getLogger(ExecutionServiceImpl.class);

	@Autowired
	public RuntimeService runtimeService;
	@Autowired
	IdentityService identityService;

	@Autowired
	CallBizService callBizService;


	@Autowired
	ProcdefNodeInfoService procdefNodeInfoService;


	@Autowired
	public ProcinstParamesService procinstParamesService;

	@Autowired
	public ProcinstFormDataService procinstFormDataService;

	@Autowired
	TaskService taskService;

	@Autowired
	SysUserService sysUserService;

	@Value(value = "${mdp.form.data-approva-uri:/lcode/formData/processApprova}")
	String formDataApprovaUri="";

	@Autowired
	PushImMsgService pushMsgService;

	/**
	 * 自定义查询，支持多表关联
	 * @param page 分页条件
	 * @param ew 一定要，并且必须加@Param("ew")注解
	 * @param ext 如果xml中需要根据某些值进行特殊处理，可以通过这个进行传递，非必须，注解也可以不加
	 * @return
	 */
	public List<Map<String,Object>> selectListMapByWhere(IPage page, QueryWrapper ew, Map<String,Object> ext){
		return baseMapper.selectListMapByWhere(page,ew,ext);
	}


	/** 请在此类添加自定义函数 
	 * @return */
	@Transactional
	public Result  start(ProcStartVo procStartVo) {


		ProcinstParames pp=procStartVo.getProcinstParames();
		User user= LoginUtils.getCurrentUserInfo();
		pp.setUserid(user.getUserid());
		if(pp.getStartDate()==null){
			pp.setStartDate(new Date());
		}
		if(StringUtils.isEmpty(pp.getDeptid())){
			pp.setDeptid(user.getDeptid());
		}
		if(StringUtils.isEmpty(pp.getMonitors())){
			pp.setMonitors(user.getUserid());
		}
		if(StringUtils.isEmpty(pp.getSponsors())){
			pp.setSponsors(user.getUserid());
		}
		FormDataVo fds=procStartVo.getFormData();
		Map<String,Object> procInstVars=this.procInstVarsHandle(pp, fds,procStartVo.getFlowVars());
		ProcessInstance processInstance;
		String[] nextAssigneeList=procStartVo.getNextAssigneeList();
		Map<String,Object> localVars=this.localVarsHandle(pp, fds,procStartVo.getFlowVars(),null);
		if(StringUtils.isEmpty(pp.getRestUrl() )  && "1".equals(pp.getIsRefForm()) && !StringUtils.isEmpty(pp.getFormId())) {
			pp.setRestUrl(formDataApprovaUri);
			localVars.put("restUrl", formDataApprovaUri);
		}
		localVars.put("currentUserid", user.getUserid());
		localVars.put("isStart", "1");
		localVars.put("currentUsername", user.getUsername());
		if(procStartVo.getNodeInfos()!=null && procStartVo.getNodeInfos().size()>0){
			localVars.put("nodeInfos", procStartVo.getNodeInfos());
		}else {
			List<ProcdefNodeInfo> procdefNodeInfos = procdefNodeInfoService.getProcdefNodeInfosFromCache(pp.getProcDefId(),pp.getBranchId());
			List<ProcinstNodeInfo> procinstNodeInfos=new ArrayList<>();
			for (ProcdefNodeInfo procdefNodeInfo : procdefNodeInfos) {
				ProcinstNodeInfo procinstNodeInfo=new ProcinstNodeInfo();
				procinstNodeInfo.setId(this.createKey("id"));
				BeanUtils.copyProperties(procdefNodeInfo,procinstNodeInfo);
			}
			procStartVo.setNodeInfos(procinstNodeInfos);
			localVars.put("nodeInfos", procStartVo.getNodeInfos());
		}
		List<ProcinstNodeInfo> procinstNodeInfos=procStartVo.getNodeInfos();
		for (ProcinstNodeInfo procinstNodeInfo : procinstNodeInfos) {
			if(!"1".equals(procinstNodeInfo.getToCreater())){
				if(StringUtils.isEmpty(procinstNodeInfo.getConditions())){
					throw new BizException("need-node-info", "当前流程未配置节点审批人，请先配置");
				}
			}
		}
		if("need-single-assignee".equals(procStartVo.getTipscode())) {
			if(nextAssigneeList!=null && nextAssigneeList.length>0) {
				localVars.put("assignee", nextAssigneeList[0]);
			}else {
				throw new BizException("need-single-assignee", "请选择执行人");
			}
		}else if("need-mulit-assignee".equals(procStartVo.getTipscode())) {
			if(nextAssigneeList!=null && nextAssigneeList.length>0) {

				List<String> assigneeList=new ArrayList<>();
				for (String assignee : nextAssigneeList) {
					assigneeList.add(assignee);
				}
				localVars.put("assigneeList", assigneeList);
			}else {
				throw new BizException("need-mulit-assignee", "请选择会签人员");
			}
		}
		try {
			identityService.setAuthenticatedUserId(user.getUserid());
			identityService.setUserInfo(user.getUserid(), "username", user.getUsername());
			identityService.setUserInfo(user.getUserid(), "branchId", user.getBranchId());
			String procDefId=procStartVo.getProcinstParames().getProcDefId();
			processInstance=runtimeService.createProcessInstanceBuilder().processDefinitionId(procDefId).businessKey(pp.getBizKey()).name( pp.getMainTitle()).transientVariables(localVars).variables(procInstVars).tenantId(user.getBranchId()).start();


		}  catch (BizException e) {
			throw e;
		}catch(FlowableIllegalArgumentException e) {
			log.error("",e);
			//org.flowable.common.engine.api.FlowableIllegalArgumentException: Variable 'assigneeList' was not found
			if("Variable 'assigneeList' was not found".equals(e.getMessage())) {
				throw new BizException("need-mulit-assignee", "请选择会签人员");
			}else {
				throw e;
			}
		}catch (FlowableException e) {
			//org.flowable.common.engine.api.FlowableIllegalArgumentException: Variable 'assigneeList':[Ljava.lang.String;@28613abf is not a Collection
			//org.flowable.common.engine.api.FlowableException: Unknown property used in expression: ${assigneeList}
			//need-mulit-assignee
			log.error("",e);
			if("Unknown property used in expression: ${assigneeList}".equals(e.getMessage())) {
				throw new BizException("need-mulit-assignee", "请选择会签人员");
			}else if("Unknown property used in expression: ${assignee}".equals(e.getMessage())){
				throw new BizException("need-single-assignee", "请选择执行人员");
			}else {
				throw e;
			}
		}catch (Exception e) {
			//org.flowable.common.engine.api.FlowableIllegalArgumentException: Variable 'assigneeList':[Ljava.lang.String;@28613abf is not a Collection
			//org.flowable.common.engine.api.FlowableException: Unknown property used in expression: ${assigneeList}
			//need-mulit-assignee
			log.error("",e);
			throw e;
		}finally {
			identityService.setAuthenticatedUserId(null);
		}
		pp.setProcInstId(processInstance.getProcessInstanceId());
		//runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), pp.getMainTitle());
		//如果有上送restUrl，回调restUrl推送审核数据 
		String restUrl=(String) localVars.get("restUrl");
		if(!StringUtils.isEmpty(restUrl)) {
			Map<String, Object> paramMap = new HashMap<String, Object>();
			///processInstance.get
			String bizKey=processInstance.getBusinessKey();
			String processInstanceId=processInstance.getProcessInstanceId();
			Map<String,Object> flowVars=procInstVars;

			String mainTitle=processInstance.getName();
			String startUserid=processInstance.getStartUserId();
			String actId="";
			String taskName="";
			String branchId=processInstance.getTenantId();
			String eventName= FlowableEngineEventType.PROCESS_STARTED.name();
			paramMap.putAll(flowVars);
			paramMap.putAll(localVars);
			paramMap.put("procInstId", processInstanceId);
			paramMap.put("startUserid", startUserid);
			paramMap.put("mainTitle", mainTitle);
			paramMap.put("assignee", "");
			paramMap.put("actId", actId);
			paramMap.put("taskName", taskName);
			paramMap.put("flowBranchId", branchId);
			paramMap.put("bizKey", bizKey);
			paramMap.put("eventName", eventName);
			paramMap.put("modelKey", processInstance.getProcessDefinitionKey());
			Tips tips=null;
			if(!StringUtils.isEmpty(pp.getFormId()) && "1".equals(pp.getIsRefForm())) {
				paramMap.put("formData", BaseUtils.toMap(fds));
				paramMap.put("formId", pp.getFormId());
				if(restUrl.indexOf(formDataApprovaUri)>=0) {
					tips=callBizService.postForTips(restUrl, paramMap);
					Result.assertIsFalse(tips);
				}else {
					tips=callBizService.postForTips(formDataApprovaUri, paramMap);
					Result.assertIsFalse(tips);
					tips=callBizService.postForTips(restUrl, paramMap);
					Result.assertIsFalse(tips);
				}

			}else {
				tips=callBizService.postForTips(restUrl, paramMap);
				Result.assertIsFalse(tips);
			}
		}
		if(StringUtils.isEmpty(pp.getId())){
			pp.setId(this.createKey("id"));
		}
		procinstParamesService.insert(pp);


		//发消息给主办/监控
		String sponsors=pp.getSponsors();
		String monitors=pp.getMonitors();

		Map<String,Object> users=new HashMap<>();
		if(!StringUtils.isEmpty(sponsors)) {
			String[] sponsorss=sponsors.split(",");
			for (String userid : sponsorss) {
				users.put(userid, userid);
			}
		}
		if(!StringUtils.isEmpty(monitors)) {
			String[] monitorss=monitors.split(",");
			for (String userid : monitorss) {
				users.put(userid, userid);
			}
		}
		users.remove(pp.getUserid());
		for (String userid : users.keySet()) {
			pushMsgService.pushMsgToIm(userid, "由您主办/监控的流程已创建,流程【"+pp.getMainTitle()+"】");
		}

		return Result.ok();

	}

	/**
	 * 激活流程
	 * @param processInstanceId
	 * @return
	 */
	public Result  activateProcessInstance(String processInstanceId){

		runtimeService.activateProcessInstanceById(processInstanceId);
		return Result.ok();
	}
	/**
	 * 挂起流程
	 * @param processInstanceId
	 * @return
	 */
	public Result  suspendProcessInstance(String processInstanceId){

		runtimeService.suspendProcessInstanceById(processInstanceId);
		return Result.ok();
	}


	/**
	 * 将一些外部变量存入工作流引擎 deptid,userid,sponsors
	 * @param procinstParames
	 * @param formData
	 * @return
	 */
	public Map<String,Object> procInstVarsHandle(ProcinstParames procinstParames,FormDataVo formData,Map<String,Object> flowVars){
		Map<String,Object> procInstVars=new HashMap<String,Object>();
		if(formData!=null && "1".equals(procinstParames.getIsRefForm()) ){
			Map<String,Object> formDataMap=BaseUtils.toMap(formData);
			procInstVars.put("formData",formDataMap);
		}
		if(flowVars!=null) {
			procInstVars.putAll(flowVars);
		}
		/**
		 procInstVars.put("deptid", procinstParames.getDeptid());
		 procInstVars.put("sponsors", procinstParames.getSponsors());
		 procInstVars.put("userid", procinstParames.getUserid());
		 **/
		return procInstVars;
	}
	/**
	 * 将一些外部变量存入工作流引擎 本地变量中，不会持久化的变量
	 * @param procinstParames
	 * @param formData
	 * @return
	 */
	public Map<String,Object> localVarsHandle(ProcinstParames procinstParames,FormDataVo formData,Map<String,Object> flowVars,Map<String,Object> localVars){
		localVars=localVars==null?new HashMap<String,Object>():localVars;
		if(flowVars!=null) {
			localVars.putAll(flowVars);
		}
		if(!StringUtils.isEmpty(procinstParames.getBizKey())) {
			localVars.put("bizKey", procinstParames.getBizKey());
		}
		if(!StringUtils.isEmpty(procinstParames.getRestUrl())) {
			localVars.put("restUrl", procinstParames.getRestUrl());
		}
		localVars.put("mainTitle", procinstParames.getMainTitle());
		localVars.put("startDeptid", procinstParames.getDeptid());
		localVars.put("sponsors", procinstParames.getSponsors());
		localVars.put("startUserid", procinstParames.getUserid());
		localVars.put("startUsername", procinstParames.getStartUsername());
		return localVars;
	}
	public List<Map<String,Object>> listProcinst(Map<String, Object> p) {
		List<Map<String,Object>> datas= this.selectListMapByWhere(null, QueryTools.initQueryWrapper(Execution.class,p),p);
		if(datas==null || datas.size()==0){
			return datas;
		}else{//assignee_,'' assignee_name
			Set<String> set=datas.stream().map(i->(String)i.get("assignee")).collect(Collectors.toSet());
			Map<String,String> usersMap=this.sysUserService.selectUserMapForSimpleInfoByUserids(set.stream().collect(Collectors.toList()));
			for (Map<String, Object> data : datas) {
				data.put("assigneeName",usersMap.get(data.get("assignee")));
			}
			return datas;
		}

	}

	public Result  delExecution(String processInstanceId,String commentMsg,Map<String,Object> localVars) {

		localVars.put("commentMsg", commentMsg);
		runtimeService.setVariablesLocal(processInstanceId, localVars);
		runtimeService.deleteProcessInstance(processInstanceId, commentMsg);
		return Result.ok();
	}

}

